package roborally.itemsonboard;

import roborally.board.Board;
import roborally.shoot.BeenShot;
import roborally.supporting.Position;
import roborally.supporting.Terminatable;
import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Raw;
import exception.IllegalPositionOnBoardException;

/**
 * A interface specifying all the needs for an item to be placed on a board. The item must have a position
 * and a board. Also the item must specify with board items can be on the same position on the same board.
 * 
 * @invar The boardItem must have a valid position at all times.
 * 		| canHaveAsPosition(this.getPosition())
 * @invar The boardItem must have a proper board at all times.
 * 		| hasProperBoard()
 * @author Molderez Tom, 2de bach Ingenieurswetenschappen Computerwetenschappen-Elektrotechniek and Reniers Jorn, 2de bach Ingenieurswetenschappen Werktuigkunde-Computerwetenschappen
 * @version 2.0
 */
public interface BoardItem extends Terminatable, BeenShot {

	/**
	 * Returns the board on which this boardItem is located, or null if it isn't located on any board.
	 */
	@Basic
	@Raw
	public abstract Board getBoard();

	/**
	 * Sets the board of this boardItem. Null is allowed.
	 * 
	 * @param board The board at which this item should be set. Null is allowed.
	 * @pre Board must be null or the board must already contain this item at the right position (so the board is Raw)
	 * 		| board==null || board.getItemsAt(this.getPosition()).contains(this)
	 * @post The board is set to the given value.
	 * 		| this.getBoard() == board
	 * @throws IllegalArgumentException
	 * 		If this is not a legal board.
	 * 		| !this.canHaveAsBoard(board)
	 */
	public void setBoard(@Raw Board board) throws IllegalArgumentException;
	
	/**
	 * Checks whether this boardItem can have the board as its board.
	 * 
	 * @param board The board to be checked.
	 * @return true if and only if board is null or the board can have this boardItem.
	 * 		| result == ( board == null || board.canHaveAsItem(this) )
	 */
	public abstract boolean canHaveAsBoard(Board board);

	/**
	 * Checks whether this boardItem has a valid board.
	 * 
	 * @return true if and only if this boardItem can have this board and or the board is null or if the board contains this item at its position.
	 * 		| if !canHaveAsBoard(getBoard())
	 * 		|	then result == false
	 * 		| else if getBoard() == null 
	 * 		|	then result == true
	 * 		| if getBoard().getItemsAt(getPosition()).contains(this)
	 * 		|	then result == true
	 * 		| else result == false
	 */
	public abstract boolean hasProperBoard();

	/**
	 * Returns the position of this boardItem.
	 */
	@Basic @Raw
	public abstract Position getPosition();

	/**
	 * Sets the position to a given value (without energy restrictions)
	 * 
	 * @param position The position to be set.
	 * @post The position is set to the given value.
	 * 		| (new this).getPosition() == position
	 * @throws IllegalArgumentException
	 * 		The boardItem can't have the given position and or the board is null or the position is null.
	 * 		| !canHaveAsPosition(position) && position == null
	 * @throws IllegalPositionOnBoardException
	 * 		The boardItem is placed on a board and can not be moved to the given position on the board.
	 * 		| !canHaveAsPosition(position) && this.getBoard() != null && position != null
	 */
	public abstract void setPosition(Position position)
			throws IllegalArgumentException, IllegalPositionOnBoardException;

	/**
	 * Controls if the position is a valid position.
	 * 
	 * @param position The position to be controlled.
	 * @return true if and only if position is not null 
	 * 					and or the board is null or the board can contain this item at this position.
	 * 		| if position == null
	 * 		|	then result == false
	 * 		| else if this.getBoard() == null
	 * 		|	then result == true
	 * 		| else 
	 * 		| 	result == canHaveAsPositionOnBoard(position)
	 */
	@Raw
	public abstract boolean canHaveAsPosition(Position position);

	/**
	 * Returns if this boardItem can have the position on his board.
	 * 
	 * @param position The position to be controlled.
	 * @pre This must have a board.
	 * 		| this.getBoard() != null
	 * @pre The position may not be null.
	 * 		| position != null
	 * @return True if the position is on the board and no other objects are obstructing the given position.
	 * 		| let temp = this.getBoard().canHaveAsPosition(position)
	 * 		| for each PositionItem in {board.getItemsAt(position)}
	 * 		|	if !canHaveItemOnSamePosition(item)
	 * 		|		then temp = false 
	 * 		| result == temp
	 */
	@Raw
	public abstract boolean canHaveAsPositionOnBoard(Position position);

	/**
	 * Returns whether this boardItem can share the same position with the given item.
	 * 
	 * @param positionItem The boardItem with which this boardItem can or can't be on the same position.
	 */
	public abstract boolean canHaveItemOnSamePosition(BoardItem positionItem);

}